package org.raymond.iworks.core.logger.interceptor;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.Buffer;
import org.raymond.iworks.commons.utils.ErrorUtil;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

@Slf4j
public class LoggingInterceptor implements Interceptor {
    private static final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS", Locale.CHINA);

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Map<String, Object> paramsMap = new HashMap<>();

        if("POST".equalsIgnoreCase(request.method())){
            RequestBody requestBody = request.body();
            if(requestBody instanceof MultipartBody requestMultipartBody){
                List<MultipartBody.Part> partList = requestMultipartBody.parts();
                for(MultipartBody.Part part : partList){
                    addPart(part, paramsMap);
                }
            }
        }

        LocalDateTime start = LocalDateTime.now().atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
        Map<String, Object> logMap = new HashMap<>();
        logMap.put("start", fmt.format(start));
        logMap.put("url", request.url().toString());
        logMap.put("request", paramsMap);

        Response copy = null;
        try{
            Response response = chain.proceed(request);

            String responseBodyString = response.body().string();
            MediaType mediaType = response.body().contentType();
            ResponseBody responseBody = ResponseBody.create(responseBodyString, mediaType);
            copy = response.newBuilder().body(responseBody).build();

            if(JSONUtil.isTypeJSONObject(responseBodyString)){
                logMap.put("response", JSONUtil.parseObj(responseBodyString));
            }if(JSONUtil.isTypeJSONArray(responseBodyString)){
                logMap.put("response", JSONUtil.parseArray(responseBodyString));
            }else{
                logMap.put("response", responseBodyString);
            }
        }catch(Exception e){
            logMap.put("error", ErrorUtil.getErrorLog(e));
        }finally {
            LocalDateTime end = LocalDateTime.now().atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
            long cost = end.toInstant(ZoneOffset.ofHours(8)).toEpochMilli()-start.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
            logMap.put("end", fmt.format(end));
            logMap.put("cost", cost+"ms");

            log.info("outLog: {}", logMap);
        }

        return copy;
    }

    private void addPart(final MultipartBody.Part part, Map<String, Object> params){
        final RequestBody body = part.body();
        final Headers headers = part.headers();
        if(headers==null || headers.size()<=0) return;

        try{
            String header = headers.value(0);
            // ["form-data";name={key}]
            // ["form-data";name={key};filename={filename}]
            String[] formDataSplits = header.replace(" ", "").replace("\"", "").split(";");
            // 普通文本
            if(formDataSplits.length==2){
                String[] nameSplits = formDataSplits[1].split("=");
                if(nameSplits.length>1){
                    String fieldname = nameSplits[1];
                    Buffer buffer = new Buffer();
                    body.writeTo(buffer);
                    String value = buffer.readUtf8();
                    params.put(fieldname, value);
                }
            }
            // 文件
            if(formDataSplits.length==3){
                String[] nameSplits = formDataSplits[1].split("=");
                String[] filenameSplits = formDataSplits[2].split("=");
                if(nameSplits.length>1 && filenameSplits.length>1) {
                    params.put(nameSplits[1], filenameSplits[1]);
                }
            }
        }catch(IOException e){
            log.error("addPart error:{}", ErrorUtil.getErrorLog(e));
        }
    }
}
